<?php

/**
 * wmfl.cn version 5.0
 * -----------------------------------------------
 * @author esinger <esinger@qq.com>
 * @link http://www.utuo88.com
 * @copyright 2014 Utuo Tech co.,Ltd
 */
namespace Utuo;

/**
 * 微信公众平台API
 */
class WechatApi
{
    // 消息类型 event/text/image/voice/video/location/link
    const TYPE_EVENT = 'event'; // 事件

    const TYPE_TEXT = 'text'; // 文本

    const TYPE_IMAGE = 'image'; // 图片

    const TYPE_VOICE = 'voice'; // 语音

    const TYPE_VIDEO = 'video'; // 视频

    const TYPE_LOCATION = 'location'; // 地理位置

    const TYPE_LINK = 'link'; // 链接

    const TYPE_MUSIC = 'music'; // 音乐消息

    const TYPE_NEWS = 'news'; // 图文消息
    
    const TYPE_TRANSFER_CUSTOMER = 'transfer_customer_service';
                              
    // 事件类型 subscribe/unsubscribe/scan/LOCATION/CLICK
    const EVENT_SUBSCRIBE = 'subscribe'; // 关注

    const EVENT_UNSUBSCRIBE = 'unsubscribe'; // 取消关注

    const EVENT_SCAN = 'SCAN'; //

    const EVENT_LOCATION = 'LOCATION'; //

    const EVENT_CLICK = 'CLICK'; //

    /**
     * 响应时间
     * 
     * @var int
     */
    private $responseTime = 0;

    /**
     * 是否开启调试
     * 
     * @var boolean
     */
    private $debug = false;

    /**
     * 接收到的数组
     * 
     * @var array
     */
    private $data = array();

    /**
     * 构造函数
     */
    public function __construct($debug = false)
    {
        $this->debug = $debug;
        $this->responseTime = 1413976469;
        if (! $this->isValid()) {
            if (isset($GLOBALS['HTTP_RAW_POST_DATA'])) {
                // 错误处理
                set_error_handler(array(
                    $this,
                    'errorHandler'
                ));
                // 处理数据
                $this->data = (array) simplexml_load_string($GLOBALS['HTTP_RAW_POST_DATA'], 'SimpleXMLElement', LIBXML_NOCDATA);
            } else {
                exit('没有接收到数据');
            }
        }
    }

    /**
     * 设置响应时间
     * 
     * @param int $responseTime            
     */
    public function setResponseTime($responseTime)
    {
        $this->responseTime = $responseTime;
    }

    /**
     * 获取请求数据
     *
     * @return string array
     */
    public function getData($name, $default = null)
    {
        if (is_string($name)) {
            return isset($this->data[$name]) ? $this->data[$name] : $default;
        }
        return $this->data;
    }

    /**
     * 获取发送方帐号（OpenID）
     * 共有数据
     *
     * @return string
     */
    public function getUserName()
    {
        return $this->data['FromUserName'];
    }

    /**
     * 获取公众平台账号
     * 共有数据
     *
     * @return string
     */
    public function getPlatName()
    {
        return $this->data['ToUserName'];
    }

    /**
     * 获取消息创建时间 （整型）
     * 共有数据
     *
     * @return integer
     */
    public function getCreateTime()
    {
        return $this->data['CreateTime'];
    }

    /**
     * 获取消息类型
     * 主要有：event/text/image/voice/video/location/link
     * 共有数据
     *
     * @return string
     */
    public function getMsgType()
    {
        return $this->data['MsgType'];
    }

    /**
     * 获取事件类型
     * 主要有：subscribe/unsubscribe/scan/LOCATION/CLICK
     *
     * @return string
     */
    public function getEvent()
    {
        return $this->getData('Event');
    }

    /**
     * 获取消息事件KEY值
     * 
     * @return string
     */
    public function getEventKey()
    {
        return $this->getData('EventKey');
    }

    /**
     * 获取消息内容
     * 
     * @return string
     */
    public function getContent()
    {
        return $this->getData('Content');
    }

    /**
     * 获取消息ID
     * 64位整型
     * 共有数据
     *
     * @return string
     */
    public function getMsgId()
    {
        return $this->getData('MsgId');
    }

    /**
     * 获取消息媒体id
     * 可以调用多媒体文件下载接口拉取数据
     * 媒体消息特有（image/voice/video）
     *
     * @return string
     */
    public function getMediaId()
    {
        return $this->getData('MediaId');
    }

    /**
     * 获取图片链接
     * 图片消息特有
     *
     * @return string
     */
    public function getImagePicUrl()
    {
        return $this->getData('PicUrl');
    }

    /**
     * 获取地理位置纬度
     * 地址位置消息特有
     *
     * @return string
     */
    public function getLocationX()
    {
        return $this->getData('Location_X');
    }

    /**
     * 获取地理位置经度
     * 地址位置消息特有
     *
     * @return string
     */
    public function getLocationY()
    {
        return $this->getData('Location_Y');
    }

    /**
     * 获取地图缩放大小
     * 地址位置消息特有
     *
     * @return string
     */
    public function getLocationScale()
    {
        return $this->getData('Scale');
    }

    /**
     * 获取地理位置信息
     * 地址位置消息特有
     *
     * @return string
     */
    public function getLocationLabel()
    {
        return $this->getData('Label');
    }

    /**
     * 获取语音格式，如amr，speex等
     * 语音消息特有
     *
     * @return string
     */
    public function getVoiceFormat()
    {
        return $this->getData('Format');
    }

    /**
     * 获取视频消息缩略图的媒体id，可以调用多媒体文件下载接口拉取数据
     * 视频消息特有
     *
     * @return string
     */
    public function getVideoThumbMediaId()
    {
        return $this->getData('ThumbMediaId');
    }

    /**
     * 获取链接标题
     * 链接消息特有
     *
     * @return string
     */
    public function getLinkTitle()
    {
        return $this->getData('Title');
    }

    /**
     * 获取链接描述
     * 链接消息特有
     *
     * @return string
     */
    public function getLinkDescription()
    {
        return $this->getData('Description');
    }

    /**
     * 获取链接地址
     * 链接消息特有
     *
     * @return string
     */
    public function getLinkUrl()
    {
        return $this->getData('Url');
    }

    /**
     * 回复消息
     * 
     * @param string $type
     *            消息类型
     * @param array|string $data
     *            消息数据
     */
    public function response($type, $data)
    {
        switch ($type) {
            case self::TYPE_TEXT:
                $this->responseText($data);
                break;
            case self::TYPE_NEWS:
                $this->responseNewsLists($data);
                break;
            case self::TYPE_MUSIC:
                $this->responseMusic($data['mediaId'], $data['title'], $data['description'], $data['url'], $data['urlHQ']);
                break;
            case self::TYPE_IMAGE:
                $this->responseImage($data);
                break;
            case self::TYPE_VOICE:
                $this->responseVoice($data);
                break;
            case self::TYPE_VIDEO:
                $this->responseVideo($data['mediaId'], $data['title'], $data['description']);
                break;
            default:
                $this->responseText($data);
        }
    }

    /**
     * 发送客服消息
     */
    public function responseCustomerService()
    {
        $format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[transfer_customer_service]]></MsgType>
</xml>
FORMAT;
        echo (sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime));
    }
    
    /**
     * 回复文本消息
     *
     * @param string $content
     */
    public function responseCustomerMsg()
    {
    	$format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[transfer_customer_service]]></MsgType>
</xml>
FORMAT;
    	exit(sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime));
    }

    /**
     * 回复文本消息
     *
     * @param string $content            
     */
    public function responseText($content)
    {
        $format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Content><![CDATA[%s]]></Content>
</xml>
FORMAT;
        exit(sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime, 'text', $content));
    }

    /**
     * 回复图片消息
     *
     * @param string $mediaId            
     */
    public function responseImage($mediaId)
    {
        $format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Image>
<MediaId><![CDATA[%s]]></MediaId>
</Image>
</xml>
FORMAT;
        exit(sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime, 'image', $mediaId));
    }

    /**
     * 回复语音消息
     *
     * @param string $mediaId            
     */
    public function responseVoice($mediaId)
    {
        $format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Voice>
<MediaId><![CDATA[%s]]></MediaId>
</Voice>
</xml>
FORMAT;
        exit(sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime, 'voice', $mediaId));
    }

    /**
     * 回复视频消息
     *
     * @param string $mediaId            
     */
    public function responseVideo($mediaId, $title, $description)
    {
        $format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Video>
<MediaId><![CDATA[%s]]></MediaId>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
</Video>
</xml>
FORMAT;
        exit(sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime, 'video', $mediaId, $title, $description));
    }

    /**
     * 回复音乐消息
     *
     * @param string $mediaId            
     */
    public function responseMusic($mediaId, $title, $description, $url, $urlHQ)
    {
        $format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[%s]]></MsgType>
<Music>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<MusicUrl><![CDATA[%s]]></MusicUrl>
<HQMusicUrl><![CDATA[%s]]></HQMusicUrl>
<ThumbMediaId><![CDATA[%s]]></ThumbMediaId>
</Music>
</xml>
FORMAT;
        exit(sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime, 'music', $title, $description, $url, $mediaId));
    }

    /**
     * 回复单图文消息
     *
     * @param string $title            
     * @param string $description            
     * @param string $picurl            
     * @param string $url            
     */
    public function responseNews($title, $description, $picurl, $url)
    {
        $format = <<<FORMAT
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>1</ArticleCount>
<Articles>
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
</Articles>
</xml>
FORMAT;
        exit(sprintf($format, $this->getUserName(), $this->getPlatName(), $this->responseTime, $title, $description, $picurl, $url));
    }

    /**
     * 回复图文消息列表，支持单图文和多图文
     *
     * @param array $newsList            
     */
    public function responseNewsLists($newsList)
    {
        $newsFormat = <<<NEWS
<xml>
<ToUserName><![CDATA[%s]]></ToUserName>
<FromUserName><![CDATA[%s]]></FromUserName>
<CreateTime>%s</CreateTime>
<MsgType><![CDATA[news]]></MsgType>
<ArticleCount>%s</ArticleCount>
<Articles>
%s
</Articles>
</xml>
NEWS;
        $itemFormat = <<<ITEM
<item>
<Title><![CDATA[%s]]></Title>
<Description><![CDATA[%s]]></Description>
<PicUrl><![CDATA[%s]]></PicUrl>
<Url><![CDATA[%s]]></Url>
</item>
ITEM;
        $item = array();
        $count = count($newsList);
        foreach ($newsList as $val) {
            $item[] = sprintf($itemFormat, $val[0], $val[1], $val[2], $val[3]);
        }
        $item = implode("\n", $item);
        exit(sprintf($newsFormat, $this->getUserName(), $this->getPlatName(), $this->responseTime, $count, $item));
    }

    /**
     * 是否验证请求
     */
    public function isValid()
    {
        return isset($_GET['echostr']);
    }

    /**
     * 验证接口
     * 
     * @param string $token            
     */
    public function valid($token)
    {
        $arr = array(
            $token,
            $_GET['timestamp'],
            $_GET['nonce']
        );
        sort($arr, SORT_STRING);
        if (sha1(implode($arr)) == $_GET['signature']) {
            exit($_GET['echostr']);
        }
    }

    /**
     * 自定义错误处理函数
     *
     * @param int $level            
     * @param string $msg            
     * @param string $file            
     * @param int $line            
     */
    public function errorHandler($errno, $errstr, $file, $line)
    {
        if ($this->debug) {
            $content = <<<FORMAT
程序出现错误：
Errno: $errno
Errstr: $errstr
File: $file
Line: $line
FORMAT;
        } else {
            $content = '程序出现错误';
        }
        $this->responseText($content);
    }
}
 
// End of file WechatApi.class.php
